package com.credithc.signature.job.service.signprovide.fadada;

import com.credithc.signature.common.constant.Constant;
import com.credithc.signature.common.constant.FddConstant;
import com.credithc.signature.common.constant.ManagerConstant;
import com.credithc.signature.common.mapper.*;
import com.credithc.signature.common.po.*;
import com.credithc.signature.common.ro.NewSignProviderReq;
import com.credithc.signature.common.ro.core.CoreRequet;
import com.credithc.signature.common.ro.facade.FddSignReq;
import com.credithc.signature.common.service.OfficialSealTemplateService;
import com.credithc.signature.common.service.SignatureLogService;
import com.credithc.signature.common.service.TemplateService;
import com.credithc.signature.common.to.CFCASignTO;
import com.credithc.signature.common.utils.AmazonUtil3;
import com.credithc.signature.common.utils.FddConfigMap;
import com.credithc.signature.common.utils.ManagerBeanUtil;
import com.credithc.signature.job.ro.JobRequest;
import com.credithc.signature.job.service.SignatureService;
import com.credithc.signature.job.service.signprovide.SignatureProvider;
import com.credithc.signature.job.utils.FddClientUtil;
import com.credithc.signature.job.utils.PathUtil;
import com.fadada.lightapi.beans.contract.ContractDownloadResponse;
import com.fadada.lightapi.beans.contract.UploadContractRsp;
import com.fadada.lightapi.beans.signauto.SignAutoReq;
import com.fadada.lightapi.beans.signauto.SignAutoRsp;
import com.fadada.lightapi.beans.signpdf.SearchInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service("fddSignature")
public class FadadaProvider implements SignatureProvider {

    @Value("${signature.snapSignPdf.path}")
    String snapPDFPath;

    @Autowired
    OfficialSealTemplateService officialSealTemplateService;

    @Autowired
    TemplateService templateService;

    @Autowired
    FddAuthPersonMapper fddAuthPersonMapper;

    @Autowired
    FddAuthCompanyMapper fddAuthCompanyMapper;

    @Autowired
    FddSignInfoMapper fddSignInfoMapper;

    @Autowired
    SignatureService signatureService;

    @Autowired
    OfficialSealTemplateMapper officialSealTemplateMapper;


    @Autowired
    FddSealInfoMapper fddSealInfoMapper;

    @Autowired
    private SignatureLogService signatureLogService;


    /**
     * 法大大签章核心方法
     *
     * @param coreRequet
     * @param noSignPdfPath
     * @param certInfos
     * @param sealCodes
     * @return
     * @throws Exception
     */
    @Override
    public String processSignature(JobRequest coreRequet, String noSignPdfPath, List<Map<String, String>> certInfos, List<String> sealCodes) throws Exception {
        log.info("开始执行法大大签章逻辑代码>>>>>>>>");
        Template template = templateService.selectByTemplateNo(coreRequet.getTemplateNo());
        String templateName = template.getTemplateName();
        Integer sealType = -1;
        if (coreRequet.getSealType() == 0 || coreRequet.getSealType() == null) {
            sealType = template.getSealType();
        } else {
            sealType = coreRequet.getSealType();
        }
        log.info("法大大签章-->签章类型：{}", sealType);
        // 1.文件上传
        String requestSn = coreRequet.getRequestSn();
        String contractId = uploadContract(requestSn, noSignPdfPath, templateName,coreRequet.getChannelNo());

        // 2.执行签章
        if (sealType.equals(Constant.PERSONAL)) {
            // 签私章
            signPersonSeal(coreRequet, contractId, certInfos);
        } else if (sealType.equals(Constant.OFFICAL)) {
            // 签公章
            signOfficialSeal(coreRequet, contractId, sealCodes);
        } else if (sealType.equals(Constant.OFFICAL_PERSONAL)) {
            // 签公章和私章
            signPersonAndOfficialSeal(coreRequet, contractId, certInfos, sealCodes);
        } else {
            log.error("－消息－:requestSn={},非法签章类型{}", requestSn, sealType);
            throw new Exception("非法签章类型");
        }
        // 3.返回文件地址
        String signSnapPDFPath = downloadContract(requestSn, noSignPdfPath, contractId,coreRequet.getChannelNo());
        log.info("执行法大大签章逻辑代码结束,文件地址={}", signSnapPDFPath);
        return signSnapPDFPath;
    }

    /**
     * 下载合同文件
     *
     * @param requestSn
     * @param noSignPdfPath
     * @param contractId
     * @return
     */
    private String downloadContract(String requestSn, String noSignPdfPath, String contractId,String channelNo) throws IOException {
        File file = new File(noSignPdfPath);
        String date = file.getParentFile().getName();
        String signPdfName = Constant.SNAP_PDF_PREFIX + requestSn + Constant.PDF_EXT;
        String signSnapPDFPath = PathUtil.getSignSnapPDFPath(snapPDFPath, signPdfName, date);
        FileOutputStream fileOutputStream = null;
        ContractDownloadResponse res = null;
        try {
            res = FddClientUtil.contractDownload(contractId,channelNo,contractId);
            byte[] content = res.getContent();
            fileOutputStream = new FileOutputStream(signSnapPDFPath);
            fileOutputStream.write(content);
        } catch (IOException e) {
            log.error("－消息－:requestSn={},调用法大大下载合同接口发生异常......", requestSn, e);
        } finally {
            fileOutputStream.close();
        }
        return signSnapPDFPath;
    }

    /**
     * 签私章+公章方法
     *
     * @param coreRequet
     * @param contractId
     * @param certInfos
     * @param sealCodes
     */
    private void signPersonAndOfficialSeal(JobRequest coreRequet, String contractId, List<Map<String, String>> certInfos, List<String> sealCodes) throws Exception {
        // 先签私章
        signPersonSeal(coreRequet, contractId, certInfos);
        // 再签公章
        signOfficialSeal(coreRequet, contractId, sealCodes);
    }

    /**
     * 签公章方法
     *
     * @param coreRequet
     * @param contractId
     * @param sealCodes
     */
    private void signOfficialSeal(JobRequest coreRequet, String contractId, List<String> sealCodes) throws Exception {
        log.info("－消息－:requestSn={},开始执行法大大【签章-公章】......", coreRequet.getRequestSn());
        String requestSn = coreRequet.getRequestSn();
        if (sealCodes == null || sealCodes.size() == 0) {
            sealCodes = new ArrayList<>();
            String templateNo = coreRequet.getTemplateNo();
            OfficialSealTemplate officialSealTemplate = new OfficialSealTemplate();
            officialSealTemplate.setTemplateNo(templateNo);
            Map<String, Object> params = ManagerBeanUtil.buildQueryParams(officialSealTemplate);
            List<OfficialSealTemplate> officialSealTemplates = officialSealTemplateMapper.selectByExample(params.get(ManagerConstant.EXAMPLE));
            for (OfficialSealTemplate sealTemplate : officialSealTemplates) {
                sealCodes.add(sealTemplate.getOfficialSealCode());
            }
        }
        List<SignAutoReq> signAutoList = buildSealCodes(coreRequet, sealCodes);
        SignAutoRsp rsp = null;
        int count = 1;
        int size = signAutoList.size();
        try {
            long start = System.currentTimeMillis();
            for (SignAutoReq req : signAutoList) {
                req.setContract_id(contractId);
                saveSignInfo(coreRequet, req, 2);
                rsp = FddClientUtil.signAuto(req,coreRequet.getChannelNo(),req.getContract_id());
                log.info("－消息－:requestSn={},第{}次调用法大大签公章接口返回={},code={},msg={},总次数={}", requestSn, count, rsp,rsp.getCode(),rsp.getMsg(), size);
                count++;
            }
            log.info("执行法大大【签章-公章】结束......耗时:{}毫秒", (System.currentTimeMillis() - start));
        } catch (Exception e) {
            log.error("－消息－:requestSn={},第{}次调用法大大签公章接口发生异常......", requestSn, count, e);
            throw e;
        }
    }

    /**
     * 签私章方法
     *
     * @param coreRequet
     * @param contractId
     * @param certInfos
     */
    private void signPersonSeal(JobRequest coreRequet, String contractId, List<Map<String, String>> certInfos) throws Exception {
        log.info("－消息－:requestSn={},开始执行法大大【签章-私章】......", coreRequet.getRequestSn());
        String requestSn = coreRequet.getRequestSn();
        if (certInfos == null || certInfos.size() == 0) {
            log.error("－消息－:requestSn={},私章数量集合为空:{}", requestSn, certInfos);
            throw new Exception("私章数量集合为空......");
        }
        List<SignAutoReq> signAutoList = buildCertInfos(certInfos);
        SignAutoRsp rsp = null;
        int count = 1;
        int size = signAutoList.size();
        try {
            long start = System.currentTimeMillis();
            for (SignAutoReq req : signAutoList) {
                req.setContract_id(contractId);
                saveSignInfo(coreRequet, req, 1);
                rsp = FddClientUtil.signAuto(req,coreRequet.getChannelNo(),req.getContract_id());
                log.info("－消息－:requestSn={},第{}次调用法大大签私章接口返回={},总次数={}", requestSn, count, rsp, size);
                count++;
            }
            log.info("执行法大大【签章-私章】结束......耗时:{}毫秒", (System.currentTimeMillis() - start));
        } catch (Exception e) {
            log.error("－消息－:requestSn={},第{}次调用法大大签私章接口发生异常......", requestSn, count, e);
        }
    }

    /**
     * 保存签章请求信息
     *
     * @param coreRequet
     * @param req
     * @param sign
     */
    private void saveSignInfo(JobRequest coreRequet, SignAutoReq req, int sign) {
        String customerId = req.getCustomer_id();
        String searchkey = req.getSearchinfo().getSearchkey();
        String templateNo = coreRequet.getTemplateNo();
        String channelNo = coreRequet.getChannelNo();
        String requestSn = coreRequet.getRequestSn();
        FddSignInfo fddSignInfo = new FddSignInfo();
        String info = sign == FddConstant.ACCOUNT_PSERSON ? "私章信息" : "公章信息";
        try {
            if (1 == sign) {
                FddAuthPerson fddAuthPerson = fddAuthPersonMapper.selectByCustomerId(customerId);
                fddSignInfo.setTransactionId(fddAuthPerson.getTransactionId());
            } else {
                FddAuthCompany fddAuthCompany = fddAuthCompanyMapper.selectByCustomerId(customerId);
                fddSignInfo.setTransactionId(fddAuthCompany.getTransactionId());
            }
        } catch (Exception e) {
            log.error("－消息－:requestSn={},保存签章请求信息-->根据customerId获取[" + info + "]发生异常, userName={},customerId{}", requestSn, searchkey, e);
        }
        fddSignInfo.setChannelNo(channelNo);
        fddSignInfo.setTemplateNo(templateNo);
        fddSignInfo.setContractId(req.getContract_id());
        fddSignInfo.setCustomerId(req.getCustomer_id());
        fddSignInfo.setRuleType(FddConstant.KEYWORDS);
        fddSignInfo.setSearchKey(req.getSearchinfo().getSearchkey());
        fddSignInfo.setContractStatus(0);
        fddSignInfo.setCreateTime(new Date());
        fddSignInfoMapper.insert(fddSignInfo);
    }

    @Autowired
    FddConfigMapper fddConfigMapper;

    /**
     * 封装公章请求信息
     *
     * @param coreRequet
     * @param sealCodes
     * @return
     */
    private List<SignAutoReq> buildSealCodes(JobRequest coreRequet, List<String> sealCodes) throws Exception {
        List<SignAutoReq> signAutoList = new ArrayList<>();
        List<CFCASignTO> officalSealInfoList = getOfficalSealInfoList2(coreRequet.getRequestSn(), coreRequet.getTemplateNo(), sealCodes);
        for (CFCASignTO cfcaSignTO : officalSealInfoList) {
            SignAutoReq signAutoReq = new SignAutoReq();
            String organization = cfcaSignTO.getUserIdNo();
           // FddAuthCompany fddAuthCompany = new FddAuthCompany();
           // fddAuthCompany.setOrganization(organization);
            //fddAuthCompany.setStatus(FddConstant.COMPANY_AUTH_SUCCESS);

            //获取appid 根据appid获取所有渠道号
            FddConfig fddConfig = FddConfigMap.getConfig().get(coreRequet.getChannelNo());
            Example e = new Example(FddConfig.class);
            Example.Criteria criteria = e.createCriteria();
            criteria.andEqualTo("appId",fddConfig.getAppId());
            List<FddConfig> fddConfigs = fddConfigMapper.selectByExample(e);
            List<String> channels = new ArrayList<>();
            for (FddConfig f:fddConfigs) {
                channels.add(f.getChannelId());
            }
            Example example = new Example(FddAuthCompany.class);
            Example.Criteria criteria1 = example.createCriteria();
            criteria1.andIn("channelNo",channels);
            criteria1.andEqualTo("organization",organization);
            criteria1.andEqualTo("status",FddConstant.COMPANY_AUTH_SUCCESS);
            example.setOrderByClause("create_time desc");
            List<FddAuthCompany> fddAuthCompanies = fddAuthCompanyMapper.selectByExample(example);
            FddAuthCompany company = null;
            if (fddAuthCompanies!=null && fddAuthCompanies.size()>0){
                company=fddAuthCompanies.get(0);
            }
            if (company == null) {
                log.error("根据organization字段查询fdd_auth_company表信息失败......");
                throw new Exception("根据organization字段查询fdd_auth_company表信息失败......");
            }
            String customerId = company.getCustomerId();
            signAutoReq.setCustomer_id(customerId);
            String keyWord = cfcaSignTO.getKeyWord();
            signAutoReq.setRuletype(FddConstant.KEYWORDS);
            SearchInfo searchInfo = new SearchInfo();
            searchInfo.setKeyx(0);
            searchInfo.setKeyy(0);
            searchInfo.setSearchkey(keyWord);
            signAutoReq.setSearchinfo(searchInfo);
            signAutoList.add(signAutoReq);
        }
        return signAutoList;
    }

    /**
     * 封装certInfos集合
     *
     * @param certInfos
     * @return
     */
    private List<SignAutoReq> buildCertInfos(List<Map<String, String>> certInfos) throws Exception {
        List<SignAutoReq> signAutoList = new ArrayList<>();
        for (Map<String, String> certInfo : certInfos) {
            SignAutoReq signAutoReq = new SignAutoReq();
            String userName = certInfo.get("userName");
            String customerId = certInfo.get("customerId");
            // 封装请求参数
            signAutoReq.setCustomer_id(customerId);
            signAutoReq.setRuletype(FddConstant.KEYWORDS);
            SearchInfo searchInfo = new SearchInfo();
            searchInfo.setKeyx(0);
            searchInfo.setKeyy(0);
            searchInfo.setSearchkey("$_" + userName + "_$");
            signAutoReq.setSearchinfo(searchInfo);
            signAutoList.add(signAutoReq);
        }
        return signAutoList;
    }

    /**
     * 合同文件上传
     *
     * @param requestSn
     * @param noSignPdfPath
     * @param templateName
     * @return
     */
    private String uploadContract(String requestSn, String noSignPdfPath, String templateName,String channelNo) {
        long start = System.currentTimeMillis();
        UploadContractRsp rsp = FddClientUtil.uploadContract(requestSn, templateName, FddConstant.DOC_TYPE, null, noSignPdfPath, null,channelNo,requestSn);
        if(rsp.getMsg().equals("操作失败")){
            for (int i = 0; i < 3; i++) {
                UploadContractRsp tempRsp = FddClientUtil.uploadContract(requestSn, templateName, FddConstant.DOC_TYPE, null, noSignPdfPath, null,channelNo,requestSn);
                if (!tempRsp.getMsg().equals("操作失败")){
                    break;
                }
            }
        }
        log.info("－消息－:requestSn={},调用法大大【上传合同】返回信息：{},耗时:{}毫秒", requestSn, rsp, (System.currentTimeMillis() - start));
        return requestSn;
    }

    public String synchronousSign(JobRequest coreRequet, String pdfPath, List<Map<String, String>> certInfos, List<String> sealCodes) throws Exception {
        log.info("法大大手动签章-->签章类型：");

        Template template = templateService.selectByTemplateNo(coreRequet.getTemplateNo());
        String templateName = template.getTemplateName();
        // 1.文件上传
        String requestSn = coreRequet.getRequestSn();
        String contractId = uploadContract(requestSn, pdfPath, templateName,coreRequet.getChannelNo());

        //签私章
        if(certInfos!=null &&certInfos.size()>0){
            signPersonSeal(coreRequet, contractId, certInfos);
        }
        //签公章
        if(sealCodes!=null &&sealCodes.size()>0){
            signOfficialSeal(coreRequet, contractId, sealCodes);
        }

        String signSnapPDFPath = downloadContract(requestSn, pdfPath, contractId,coreRequet.getChannelNo());
        File file1 = new File(signSnapPDFPath);

        AmazonUtil3.uploadToS3(file1, contractId);
        SignatureLog signatureLog = new SignatureLog();
        signatureLog.setRequestSn(requestSn);
        // 设置签章成功
        signatureLog.setSignStatus(2);
        signatureLog.setContractUri(contractId);
        signatureLogService.updateByReqSNSelective(signatureLog);
        return contractId;
    }

    public String contractRenewal(FddSignReq req,JobRequest coreRequet,String noSignPdfPath) throws Exception {
        NewSignProviderReq jsonBody = req.getJsonBody();
        List<Map<String, String>> certInfos = jsonBody.getCertInfos();
        List<String> officialSealsCodes = jsonBody.getOfficialSealsCodes();
        if (certInfos != null && certInfos.size() > 0) {
            signPersonSeal(coreRequet,req.getContractId(),certInfos);
        }
        if (officialSealsCodes!=null && officialSealsCodes.size()>0) {
            coreRequet.setTemplateNo("续签不记录模板编号");
            signOfficialSeal(coreRequet,req.getContractId(),officialSealsCodes);
           // List<SignAutoReq> signAutoReqs = buildSealCodes(coreRequet, officialSealsCodes);
        }
        // 3.返回文件地址
        String signSnapPDFPath = downloadContract(req.getRequestSn(), noSignPdfPath, req.getContractId(),coreRequet.getChannelNo());
        log.info("执行法大大签章逻辑代码结束,文件地址={}", signSnapPDFPath);
        return signSnapPDFPath;
    }

    /**
     * 获取公章相关信息--法大大
     *
     * @param requestSn
     * @param templateNo
     * @return
     * @throws Exception
     */
    public List<CFCASignTO> getOfficalSealInfoList2(String requestSn, String templateNo, List<String> sealCodes) throws Exception {
        List<CFCASignTO> collect = new ArrayList<>();
        List<FddSealInfo> sealInfos = new ArrayList<>();
        List<String> sealCodeList = new ArrayList<>();
        if (sealCodes == null) {
            OfficialSealTemplate officialSealTemplate = new OfficialSealTemplate();
            officialSealTemplate.setTemplateNo(templateNo);
            List<OfficialSealTemplate> officialSealTemplates = officialSealTemplateService.selectByExample(officialSealTemplate);
            if (officialSealTemplates == null || officialSealTemplates.size() == 0) {
                log.error("－消息－:requestSn={},签章公章出现异常,该模板没有公章", requestSn);
                throw new Exception("模板没有公章");
            }
            sealCodeList = officialSealTemplates.stream().map(sealTemplate -> sealTemplate.getOfficialSealCode()).collect(Collectors.toList());
            sealInfos = fddSealInfoMapper.selectBySealCodeList(sealCodeList);
        } else if (sealCodes.isEmpty()) {
            sealInfos = new ArrayList<>();
        } else {
            sealInfos = fddSealInfoMapper.selectBySealCodeList(sealCodes);
        }

        for (FddSealInfo fddSealInfo : sealInfos) {
            if (StringUtils.isBlank(fddSealInfo.getKeyWords())) {
                log.error("－消息－:requestSn={},公章关键信息没有找到,请在系统配置管理中配置", requestSn);
                throw new RuntimeException("公章关键信息没有找到,请在系统配置管理中配置");
            }
            if (fddSealInfo.getStatus() == 0) {
                log.error("－消息－:requestSn={},公章状态不可用,请检查", requestSn);
                throw new RuntimeException("公章状态不可用,请检查");
            }
            SealInfo sealInfo = new SealInfo();
            BeanUtils.copyProperties(fddSealInfo, sealInfo);
            CFCASignTO cfcaSignTO = generateCFCASignTO(requestSn, sealInfo);
            collect.add(cfcaSignTO);
        }
        return collect;
    }

    /**
     * 拼装CFCASignTO
     *
     * @param requestSn
     * @param sealInfo
     * @return
     * @throws Exception
     */
    private CFCASignTO generateCFCASignTO(String requestSn, SealInfo sealInfo) throws Exception {
        CFCASignTO cfcaSignTO = new CFCASignTO();
        cfcaSignTO.setKeyWord(sealInfo.getKeyWords());
        cfcaSignTO.setSealPassword(sealInfo.getSealPwd());
        cfcaSignTO.setSealName(sealInfo.getSealName());
        cfcaSignTO.setSealPerson(sealInfo.getSealPerson());
        cfcaSignTO.setUserName(sealInfo.getSealPerson());
        cfcaSignTO.setIdTypeCode(sealInfo.getIdentificationType());
        cfcaSignTO.setUserIdNo(sealInfo.getIdentificationNo());
        cfcaSignTO.setOrganCode(sealInfo.getSealOrg());
        cfcaSignTO.setSealImage(sealInfo.getImage());
        if (StringUtils.isBlank(sealInfo.getSealCode())) {
            log.error("－消息－:requestSn={},公章不存在", requestSn);
            throw new Exception("公章不存在");
        }
        cfcaSignTO.setSealCode(sealInfo.getSealCode());
        return cfcaSignTO;
    }

}
